<?php
/**
 * Application model for Cake.
 *
 * This file is application-wide model file. You can put all
 * application-wide model-related methods here.
 *
 * PHP versions 4 and 5
 *
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
 * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright     Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
 * @link          http://cakephp.org CakePHP(tm) Project
 * @package       cake
 * @subpackage    cake.app
 * @since         CakePHP(tm) v 0.2.9
 * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
 */

/**
 * Application model for Cake.
 *
 * Add your application-wide methods in the class below, your models
 * will inherit them.
 *
 * @package       cake
 * @subpackage    cake.app
 */
class AppModel extends Model {
  
    
  // Add the ContainableBehavoir
  var $actsAs = array('Containable');
  
  /**
   *
   * @var array $optionalAssocModelsAndFields   List of Model=>field pairs that are 
   *                                            directly associated with a model 
   *                                            (hasOne, hasMany), but are not logically required
   *                                            to exist with this model. This list is used by
   *                                            funcs like 'removeBlankOptionalFields()', which
   *                                            handles preparing the 'data' array for saving.
   */
  var $optionalAssocModelsAndFields = array();
  
  
  
  
    
  /**
   * Removes empty OPTIONAL fields from the $data array, so that a saveAll() won't even try to save to 
   * the associated model. Relies on an associated model's $optionalAssocModelsAndFields array already 
   * being set.
   * 
   * @param array $data                   Reference to the data array that you wish to have the blank
   *                                      fields removed from.
   * @param boolean $deleteBlankRecords  Should a blank field also be deleted from its database table? This
   *                                     is useful when a user is editing a form, and chooses to blank out
   *                                     an optional form field.
   *                                     If this param is true, a delete will be performed, using an 'id',
   *                                     such as: Job->Modelname->delete($this->data[Modelname]['id'].
   */
  function removeBlankOptionalFields(&$data, $deleteBlankRecords = false ) {

    foreach ($this->optionalAssocModelsAndFields as $Modelname => $fieldname) {
      //pr("removeBlankOptionalFields() now looking at Model,Fieldname: $Modelname, $fieldname");
      
      if(is_array($fieldname)) {
        foreach($fieldname as $field) {
          $this->removeModelsWithBlankField($Modelname, $field, $data, $deleteBlankRecords);
        }
      }
      else {
        $this->removeModelsWithBlankField($Modelname, $fieldname, $data, $deleteBlankRecords);
      }
    } // end foreach optionalAssocModelsAndFields item
    
  } ////////////////////// end removeBlankOptionalFields() //////////////////////////
  
  /**
   * Helper func for removeBlankOptionalFields().  
   * 
   * @param string $Modelname
   * @param string $fieldname
   * @param array $data 
   */  
  function removeModelsWithBlankField($Modelname, $fieldname, &$data, $deleteBlankRecords = false) {

    ///////////////////////////////////////////////
    ///// Handle hasOne type associated Models ////
    if( ! isset($this->hasMany[$Modelname])) { // if the model is a hasOne
      if(    isset($data[$Modelname]) && isset($data[$Modelname][$fieldname])
          && empty($data[$Modelname][$fieldname]) && !is_numeric($data[$Modelname][$fieldname]) ) {
                        // need the !is_numeric() check b/c a var with val 0 is considered "empty".
        // Also DELETE the database record if:
        //   (1) in edit mode and,
        //   (2) field is blank (the outer if catches that) and,
        //   (3) the edited record orignally HAD this optional table value to begin with - 
        //         a numeric id field is included if the record had an id to begin with 
        //        (an optional field had a value).
        if($deleteBlankRecords && isset($data[$Modelname]['id']) && is_numeric($data[$Modelname]['id']) ) { 

          if ($this->$Modelname->delete($data[$Modelname]['id'])) {
            //$this->Session->setFlash("$Modelname $fieldname deleted");
          }
          else {
            //$this->Session->setFlash("$Modelname $fieldname NOT deleted successfully");
            pr("$Modelname $fieldname NOT deleted successfully");
          }
        } // end if deleteBlankRecords

        unset($data[$Modelname]); // now saveAll(this->data) won't try saving in $Modelname table
      } // end if field is empty
    } // end handle hasOne associated
    ////////////////////////////////////////
    //// Handle HASMANY associations ///////
    else if(isset($this->hasMany[$Modelname]) && isset($data[$Modelname]) ) {

      foreach($data[$Modelname] as $hasManyIndex => $fields) {

        if(    isset($data[$Modelname][$hasManyIndex][$fieldname]) 
            && empty($data[$Modelname][$hasManyIndex][$fieldname])) {
          // Also DELETE the database record if:
          //   (1) in edit mode and,
          //   (2) field is blank (the outer if catches that) and,
          //   (3) the edited record orignally HAD this optional table value to begin with - 
          //         a numeric id field is included if the record had an id to begin with 
          //        (an optional field had a value).
          if($deleteBlankRecords &&      isset($data[$Modelname][$hasManyIndex]['id']) 
                                 && is_numeric($data[$Modelname][$hasManyIndex]['id']) ) { 

            if ($this->$Modelname->delete($data[$Modelname][$hasManyIndex]['id'])) {
              //$this->Session->setFlash("$Modelname $fieldname deleted");
            }
            else {
              //$this->Session->setFlash("$Modelname $fieldname NOT deleted successfully");
              pr("$Modelname $fieldname NOT deleted successfully");
            }
          } // end if deleteBlankRecords

          unset ($data[$Modelname][$hasManyIndex]); // now saveAll(this->data) won't try saving in $Modelname table
        } // end if field is empty
      } // end foreach set of fields for this hasMany model
      // If all numerically indexed items of the hasMany model have been removed, then remove the entire
      //  model from the data array too:
      if( empty($data[$Modelname] )) {
        unset ($data[$Modelname]); // now saveAll(this->data) won't try saving in $Modelname table
      }
    } // end handling hasMany associated models    
  } ///////////////////// end removeModelsWithBlankField() ////////////////
  
  /**
   *
   * @param type $data 
   */
  function handleDeletingJobTypeRecords(&$data) {
    
    // Get database's version of the edited JobVersion's data so we can compare to the incoming edited data
    $conditions = array('JobVersion.id' => $data['JobVersion'][0]['id'] );
    $prevJobVersionArray = $this->find('first', array('conditions'=>$conditions ));
    //debug($prevJobVersionArray);
    
    // If edited version has different job type than the database version, delete db version records
    if($prevJobVersionArray['JobVersion']['job_type_id']  != $data['JobVersion'][0]['job_type_id']) {
      
      // delete prev version's db records
      switch($prevJobVersionArray['JobType']['config_file_val']) {
        
        case 'FDK':
          $basis_id = $prevJobVersionArray['ParamFdk']['param_fdk_base_id'];
          $connector_id = $prevJobVersionArray['ParamFdk']['id'];
          
          $this->ParamFdk->delete($connector_id);
          $this->ParamFdk->ParamFdkBasis->delete($basis_id);
          break;
        case 'PSIRT':
          // intentionally fall through to next
        case 'SIRT':
          $basis_id = $prevJobVersionArray['ParamSirt']['param_sirt_base_id'];
          $connector_id = $prevJobVersionArray['ParamSirt']['id'];
          
          $this->ParamSirt->delete($connector_id);
          $this->ParamSirt->ParamSirtBasis->delete($basis_id);
          break;
        case 'RECON_NONE':
          break;
      }
    }
    // If job type remained the same, just delete record out of the 'connector' table, since
    //  the save section will automatically add a new record to the 'connector' table, even when
    //  the edited job type is the same.  UPDATE: see note below.
    else {
      switch($prevJobVersionArray['JobType']['config_file_val']) {
        case 'FDK':
          $connector_id = $prevJobVersionArray['ParamFdk']['id'];
          $this->ParamFdk->delete($connector_id);
          break;
        case 'PSIRT':
          // intentionally fall through to next
        case 'SIRT':
          $connector_id = $prevJobVersionArray['ParamSirt']['id'];
          $this->ParamSirt->delete($connector_id);
          break;
        case 'RECON_NONE':
          break;
      }
    }
    // UPDATE Note:  I later found it necessary to use Cake's callbacks 'beforeDelete' and 'afterDelete'
    //  to cascade a deletion of a 'connector' table row to the base table row (i.e., when a delete call
    //  comes in to ParamFdk, need to delete the corresponding ParamFdkBasis row).  Therefore,
    //  the above calls to delete only the 'connector' table row may actually delete the 'base' table
    //  row as well.
    
  } ////////// end handleDeletingJobTypeRecords() //////////
  
  
  /**
   * Performs a save() on associated hasOne and hasMany tables that have data set in the array
   * parameter passed in (skips related tables that have no field set like 'data[className]').  
   * 
   * This should be called AFTER the main model successfully saved its data, so that 'this->id'
   * refers to the id of the record being referred to by the foreign key.  This func
   * then takes the id of that table row to use as the foreign key for related tables.
   * 
   * @author Jason Frank 8/3/2011
   * @param array   $data   The array that contains data to save to associated tables.
   * @param boolean $saveRecursive
   */
  function saveAllAssociated(&$data = array(), $saveRecursive = true, $printError = false) {

    // save hasOne associated models first
    foreach ($this->hasOne as $assocAlias => $assocArray) {

      $className  = $assocArray['className'];
      $foreignKey = $assocArray['foreignKey'];
      
      if( isset($data[$className])) {

        $data[$className][$foreignKey] = $this->id;
        $this->$className->create(); // initializes a new Model for inserting into DB
        //$saved = $this->$className->saveAll($data[$className], array('validate'=>'first'));
        
        //pr("About to save Class name: $className");
        $saved = $this->$className->save($data, array('validate'=>'first'));
        if($saved) {   //pr("saveAllAssoc()'s saveAll suppossedly worked.  Returned:  $saved");
          // Recursively save this model's associated models too:
          if($saveRecursive) {
            $recursiveSaved = $this->$className->saveAllAssociated($data);
            if(! $recursiveSaved) return false;
          }
        }
        else {
          //$this->Session->setFlash(__('The job could not be saved. Please, try again.', true));
          if($printError) {
            pr("{$className}->save() Failed in saveAllAssociated(). retval was: "); pr($saved); 
            pr("Invalid fields: "); pr($this->$className->invalidFields());
          }
          return false;
        }
      }
    } /// end iterating through hasOne's

    // Save hasMany's.  The data array has an extra index.
    foreach ($this->hasMany as $assocAlias => $assocArray) {

      $className  = $assocArray['className'];
      $foreignKey = $assocArray['foreignKey'];         //pr("Class name: $className");  
      // Iterate through each instance of fields of this hasMany model that has any values in $data
      if(isset($data[$className])) {
        foreach ($data[$className] as $i => $values) {

          $data[$className][$i][$foreignKey] = $this->id;
          $this->$className->create(); // initializes a new Model for inserting into DB

          $saved = $this->$className->save($data[$className][$i], array('validate'=>'first'));
          if($saved) { //pr("saveAllAssoc()'s saveAll suppossedly worked.  Returned: $saved");
            if($saveRecursive) {
              $recursiveSaved = $this->$className->saveAllAssociated($data);
              if(! $recursiveSaved) return false;
            }
          }
          else {
            //$this->Session->setFlash(__('The job could not be saved. Please, try again.', true));
            if($printError) {
              pr("{$className}->save() Failed in saveAllAssociated(). retval was: "); pr($saved); 
              pr("Invalid fields: "); pr($this->$className->invalidFields());
            }
            return false;
          }
        } // end foreach set of fields 
      } // end if this hasMany is set in the $data array 
    } // end foreach hasMany association
    
    return true;
  } ///////////// end func saveAllAssociated() //////////////// 
  
  
  function validateAllAssociated(&$data = array(), $saveRecursive = true, $printError = false) {
    
    foreach ($this->hasOne as $assocAlias => $assocArray) {

      $className  = $assocArray['className'];
      $foreignKey = $assocArray['foreignKey'];
      
      $validated = $this->$className->saveAll($data, array('validate' => 'only'));
      if($validated) {   //pr("saveAllAssoc()'s saveAll suppossedly worked.  Returned:  $saved");    
        // Recursively save this model's associated models too:
        if($saveRecursive) {
          $recursiveValidated = $this->$className->validateAllAssociated($data, $saveRecursive, $printError);
          if( ! $recursiveValidated) return false;
        }
      }
      else {
        if($printError) {
          pr("{$className}->saveAll('validate'=>'only') Failed in validateAllAssociated(). retval was: "); 
          pr($validated); 
          pr("Invalid fields: "); pr($this->$className->invalidFields());
        }
        return false;
      }
    }
    foreach ($this->hasMany as $assocAlias => $assocArray) {
      $className  = $assocArray['className'];
      $foreignKey = $assocArray['foreignKey'];  
      // Iterate through each instance of fields of this hasMany model that has any values in $data
      if(isset($data[$className])) {
        foreach ($data[$className] as $i => $values) {
          
          $validated = $this->$className->saveAll($data[$className][$i], array('validate' => 'only'));
          if($validated) {   //pr("saveAllAssoc()'s saveAll suppossedly worked.  Returned:  $saved");
            // Recursively save this model's associated models too:
            if($saveRecursive) {
              $recursiveValidated = $this->$className->validateAllAssociated($data, $saveRecursive, $printError);
              if( ! $recursiveValidated) return false;
            }
          }
          else {
            if($printError) {
              pr("{$className}->saveAll('validate'=>'only') Failed in validateAllAssociated(). retval was: "); 
              pr($validated); 
              pr("Invalid fields: "); pr($this->$className->invalidFields());
            }
            return false;
          }
        }
      }  
    }
    return true;
  } ////////////// end func validateAllAssociated() ////////////
  
  
  function validateAllAssociated_2(&$data = array(), $saveRecursive = true, $printError = false) {
    
    $hasOneValidated = true;
    $hasOneRecurValidated = true;
    $hasManyValidated = true;
    $hasManyRecurValidated = true;
    pr("-----------------------------------------------------------");
    pr("------ Top of validateAllAssoc2 - Model: $this->name ------");
    
    foreach ($this->hasOne as $assocAlias => $assocArray) {

      $className  = $assocArray['className'];
      $foreignKey = $assocArray['foreignKey'];
      
      $hasOneValidated = $hasOneValidated && $this->$className->saveAll($data, array('validate' => 'only'));
      if($hasOneValidated) {   
        pr("validateAllAssoc_2()'s {$className}->saveAll('validate'=>'only') suppossedly worked.  Returned:  $hasOneValidated");    
      }
      else {
        if($printError) {
          pr("{$className}->saveAll('validate'=>'only') Failed in validateAllAssociated(). retval was: "); 
          pr($hasOneValidated); 
          pr("Invalid fields: "); pr($this->$className->invalidFields());
        }
      }
      // Recursively save this model's associated models too:
      if($saveRecursive) {
        $hasOneRecurValidated = $this->$className->validateAllAssociated_2($data, $saveRecursive, $printError);
      }
    }
    foreach ($this->hasMany as $assocAlias => $assocArray) {
      $className  = $assocArray['className'];
      $foreignKey = $assocArray['foreignKey'];  
      // Iterate through each instance of fields of this hasMany model that has any values in $data
      if(isset($data[$className])) {
        foreach ($data[$className] as $i => $values) {
          
          $hasManyValidated = $hasManyValidated && $this->$className->saveAll($data[$className][$i], array('validate' => 'only'));
          if($hasManyValidated) {   
            pr("validateAllAssoc_2()'s {$className}->saveAll('validate'=>'only') suppossedly worked.  Returned:  $hasManyValidated");
          }
          else {
            if($printError) {
              pr("{$className}->saveAll('validate'=>'only') Failed in validateAllAssociated(). retval was: "); 
              pr($hasManyValidated); 
              pr("Invalid fields: "); pr($this->$className->invalidFields());
            }
          }
          // Recursively save this model's associated models too:
          if($saveRecursive) {
            $hasManyRecurValidated = $this->$className->validateAllAssociated_2($data, $saveRecursive, $printError);
          }
        }
      }  
    }
    pr("------- end of func for Model: $this->name --------");
    pr("---------------------------------------------------");
    return $hasOneValidated && $hasOneRecurValidated && $hasManyValidated && $hasManyRecurValidated;
  } ////////////// end func validateAllAssociated() //////////// 
  
  
  /**
   * Gets a value from the database table specified by $Model. Note that it only searches that table,
   * not any associated tables.
   *
   * @param string $Model
   * @param string $field
   * @param int $id
   * @return value from database 
   */
  function getModelFieldValFromId($Model, $field, $id) {
    
    //$this->$Model->recursive = -1;
    $this->recursive = -1;
    $conditions = array("$Model.id" => $id );
    
            //    debug("About to perform the find(), with conditions & Model & field:");
            //    debug($conditions);
            //    debug($Model);
            //    debug($field);
    
    $foundArray = $this->find('first', array('conditions'=>$conditions ));
    return $foundArray[$Model][$field];
  } /////////////// end getJobFieldValFromId() ////////////
  
  
  
  /**
   * Returns an array of dir names from a given full path base dir.  The list will not
   * contain filenames that are not dirs, and will not contain "." or ".."
   * 
   * @param string $dirNamesToExclude   This would typically be a "special dir" or two that should
   *                                    not be included in the returned list.  An example would
   *                                    be something like a "Preview Dir" in the Job Groups dir. 
   */
  function getDirNamesFromBase($baseFullPath, $dirNamesToExclude = array()) {
    
    $dirsToReturn = array();
    $files = scandir($baseFullPath);
    
    // Strip out ".", "..", non-dirs, and dirs that should be excluded
    foreach ($files as $file) {
      $fileFullPath = $baseFullPath . DS . $file;
      
      if(is_dir($fileFullPath) && $file != "." && $file != "..") {
        if(is_array($dirNamesToExclude) && !array_key_exists($file, $dirNamesToExclude)) {
          $dirsToReturn[] = $file;
        }
        else if($file != $dirNamesToExclude) {
          $dirsToReturn[] = $file;
        }
      }
    }
    
    return $dirsToReturn;
  } /////////// end getDirNamesFromBase() //////////
  
  /**
   *
   * @param array   $dirList     List of dir names to check whether or not each name is already in the
   *                             database or not (specify whether you want them already in db with $getNew).
   * @param string  $dirField    The db field containing the dir name to search against (it is assumed that
   *                             this field is in the model on which this method was called).
   * @param boolean $getNew      Either you want to search for dirs not already in db (true), or
   *                             you want to search for dirs that ARE already in db (false, i.e., 'get old').
   * @param string  $includeDir  In the case of searching for new dirs ($getNew = true), this is a 
   *                             dir name to include in the results if it exists in $dirList, even
   *                             though it is already in the db.  This would used when the user wants
   *                             to edit a Job Group and their orig dir still exists - we want to enable
   *                             them to re-assign their original dir name choice.
   */
  function getNewOrOldDirNamesRelativeToDB($dirList, $dirField, $getNew = true, $includeDir = null) {
    
    $dirsToReturn = array();
    
    // Get database records first
    $fieldsToGet = array($dirField);
    $this->recursive = -1;
    $dbDirs = $this->find('all', array('fields'=>$fieldsToGet));
        //debug($dbDirs);
    
    // Find matches between supplied dir list and db records
    foreach($dirList as $dir) {
      
      $addThisDir = false;
      $dirAlreadyInDB = false;
      
      foreach($dbDirs as $dbDirArray) {
        // If this dir exists in the db...
        if($dir == $dbDirArray[$this->name][$dirField]) {
          
          $dirAlreadyInDB = true;
          // special case: dir exists in db and we only want non-existing dirs, but we shall include it
          //  if it is the $includeDir
          if($getNew && $includeDir == $dir) { 
            $addThisDir = true;
          }
          // Dir exists in db and we DO want existing dirs (already in db)
          else if(!$getNew) {
            $addThisDir = true;
            //$pr("Found match and you wanted existing db dir... will add dir: $dir");
          }
        }
      } // end iterating through the db dirs
      
      // If we want new dirs and the dir is in fact not already in the db, then include it
      if($getNew && !$dirAlreadyInDB) {
        $addThisDir = true;
      }
      
      if($addThisDir) {
        $dirsToReturn[] = $dir;
      }
    } // end iterating through the dirs in the given list
    
    return $dirsToReturn;
  } ////////////// end getNewOrOldDirNamesRelativeToDB() ////////////////////////////
  
  
  
  
  
  /**
   * Validates a value to ensure it is between $lowerBound and $upperBound.  You can
   * set whether or not the upper bound or lower bound value should pass validation (i.e., should
   * the range be inclusive or exclusive of the bounds).  If you wish to not have an upper bound
   * (upper bound is infinity), then pass null as the upperBound.
   * 
   * Specify this function in the model validation rule.  Note that with these validation functions,
   * Cake automatically passes in the first param ($keyValArray).
   * 
   * @author Jason Frank
   * @param array $keyValArray   The single key/val that is being validated
   * @param type $keyToCheck     The key name 
   * @param type $lowerBound
   * @param type $upperBound
   * @param boolean $lowerBoundIsOK  Should the lower bound be considered OK? (If false, a value at the lower bound will not validate)
   * @param boolean $upperBoundIsOK  Should the upper bound be considered OK? (If false, a value at the upper bound will not validate) 
   */
  public function validateRange($keyValArray,    $keyToCheck, 
                                $lowerBound,     $upperBound, 
                                $lowerBoundIsOK, $upperBoundIsOK) {
    
      //    debug("\nInside validateRange(), before checking val.");
      //    debug($keyValArray);
      //    debug($keyToCheck);
      //    debug($lowerBound);
    
    $valToCheck = $keyValArray[$keyToCheck];
    // If there is an upper bound)
    if($upperBound != null) {
      
      if($lowerBoundIsOK && $upperBoundIsOK ) {
        if($valToCheck < $lowerBound || $valToCheck > $upperBound) { return false; }
        else {  return true;  }
      }
      else if($lowerBoundIsOK) {
        if($valToCheck < $lowerBound || $valToCheck >= $upperBound) { return false; }
        else {  return true;  }
      }
      else if($upperBoundIsOK) {
        if($valToCheck <= $lowerBound || $valToCheck > $upperBound) { return false; }
        else {  return true;  }
      }
      else {
        if($valToCheck <= $lowerBound || $valToCheck >= $upperBound) { return false; }
        else {  return true;  }
      }
    }
    // If there is no upper bound
    else {
      if($lowerBoundIsOK) {
        if($valToCheck < $lowerBound) { return false; }
        else {  return true;  }
      }
      else {
        if($valToCheck <= $lowerBound) { return false; }
        else {  return true;  }
      }
    }
  } //////////// end function validateRange()  //////
  
  
  /**
   * Validates a value to ensure it is an INTEGER between $lowerBound and $upperBound INCLUSIVE.  
   * If the upperBound is not provided, only the lowerBound is checked.
   * Specify this function in the model 'validate' array as the validation func.  Note that the 
   * first argument '$keyValToCheck' gets passed in by Cake automatically, while the other
   * args must be specified in the validation rule.
   * 
   * @author Jason Frank
   * @param array $keyValToCheck   The single key/val that is being validated
   * @param type $keyToCheck     The key name 
   * @param type $lowerBound     The lower integer that is considered OK and should validate.
   * @param type $upperBound     (optional) The upper integer that is considered OK and should validate.
   */
  public function validateIntRange($keyValToCheck, $keyToCheck, $lowerBound, $upperBound = NULL)  {
    
      //    debug("\nInside validateRange(), before checking val.");
      //    debug($keyValArray);
      //    debug($keyToCheck);
      //    debug($lowerBound);
    
    $valToCheck = $keyValToCheck[$keyToCheck];
    
    // First check that the value is an integer
    $noWhitespaceVal = trim($valToCheck);
    $pattern = '/^(\-)?[0-9]{1,}$/';  // One or more digits with optional "-" at beginning
    if(preg_match($pattern, $noWhitespaceVal)) {
      // proceed to other checks below 
    }
    else {
      return false;
    }
 
    // Now check that the val is in proper range
    if($upperBound !== NULL) {

      if($valToCheck < $lowerBound || $valToCheck > $upperBound) { return false; }
      else {  return true;  }
    }
    else {
      if($valToCheck < $lowerBound) { return false; }
      else { return true; }
    }
  } //////////// end function validIntRange()  //////


  /**
   * Validates a value to ensure it is an INTEGER.
   * Specify this function in the model 'validate' array as the validation func.  Note that the 
   * first argument '$keyValToCheck' gets passed in by Cake automatically, while the other
   * args must be specified in the validation rule.
   * 
   * @author Jason Frank
   * @param array $keyValToCheck   The single key/val that is being validated
   * @param type $keyToCheck       The key name 
   */
  public function validateInt($keyValToCheck, $keyToCheck)  {
    
      //    debug("\nInside validateRange(), before checking val.");
      //    debug($keyValArray);
    
    $valToCheck = $keyValToCheck[$keyToCheck];
    
    // First check that the value is an integer
    $noWhitespaceVal = trim($valToCheck);
    $pattern = '/^(\-)?[0-9]{1,}$/';  // One or more digits with optional "-" at beginning
    if(preg_match($pattern, $noWhitespaceVal)) {
      return true;
    }
    else {
      return false;
    }

  } //////////// end function validInt()  //////
  
  /**
   * Validates 1 or more fields such that the group of fields are unique.
   * This will be used for example to verify that a pair of database columns
   * is unique (where maybe one column is like a namespace).
   * 
   * http://stackoverflow.com/questions/2461267/cakephp-isunique-for-2-fields
   * 
   * @param string $data Unused ($this->data is used instead).
   * @param mnixed $fields field name (or array of field names) to validate .
   * @return boolean true if combination of fields is unique for the database table.
  */ 
  function checkUnique($data, $fields) { 
    
    if (!is_array($fields)) { 
      $fields = array($fields); 
    } 
    foreach($fields as $key) { 
      $tmp[$key] = $this->data[$this->name][$key]; 
    } 
    // Deal with doing UPDATES (see if the data already has an id set).
    if (isset(     $this->data[$this->name][$this->primaryKey]) 
         && !empty($this->data[$this->name][$this->primaryKey]) ) { 
      //$tmp[$this->primaryKey] = "<>". $this->data[$this->name][$this->primaryKey]; 
      $tmp[$this->primaryKey] = "!=". $this->data[$this->name][$this->primaryKey]; 
    } 
    return $this->isUnique($tmp, false); 
    //return $this->hasAny($tmp);
                
  } /////////// end func checkUnique() /////////////////////// 
  
  
  
  /**
   *
   * @param type $data
   * @param type $fields 
   */
  function myCheckUnique($data, $fields) {
    
    //debug($this->data[$this->name]);
    
    $this->recursive = -1;
    $conditions = array();
    $fieldsToSearch = array();
    $foundArray = array();
    
    if (!is_array($fields)) { 
      $fields = array($fields); 
    }
    // Set field/value pairs of the form 'MyModel.fieldname'=>value
    // Also set fieldsToSearch (to limit # of fields queiried for) to form 'MyModel.fieldname'.
    foreach($fields as $field) { 
      $conditions[$this->name . ".". $field] = $this->data[$this->name][$field];
      $fieldsToSearch[] = $this->name . ".". $field;
    } 
    $fieldsToSearch[] = 'id';

    $foundArray = $this->find('first', array('conditions'=>$conditions, 'fields'=>$fieldsToSearch));
    //debug($foundArray);
    
    // If a match was found, see if it is the same record that was originally stored
    if(!empty($foundArray)) {
      // If this is the same record that was orig stored (same id), that just means that 
      // the user is editing this record and wants to save again, which is fine.
      if(isset($this->data[$this->name]['id']) 
            && $foundArray[$this->name]['id'] == $this->data[$this->name]['id']) {
        return true;
      }
      // else if diff record wants to store same combination of fields, no dice!
      else {
        return false;
      }
    }
    // if no match was found
    else {
      return true;
    }
  
  } /////////////// end function myCheckUnique() ///////////////
  
} ////////////////////////////// end class ////////////////////////////////////////////////